home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Programmer Power Tools
/
Programmer Power Tools.iso
/
info
/
memlist.arc
/
MEMLIST.ASM
next >
Wrap
Assembly Source File
|
1988-09-04
|
12KB
|
418 lines
comment |
This program unravels DOS's (on most machines, Versions 2.0 up to 3.3)
memory control blocks (MCBs) and prints the information it finds.
If run under DOS 3.0 or later, it will also print the name it can
find for each program in memory. The commenting and wording of
printing messages have been changed in a number of places.
DOS allocates blocks in 16-byte paragraphs, so shift the Block size
reported by the program 4 bits left (multiply by 16!) to determine
the actual block length in bytes.
Microsoft has never officially documented how MSDOS allocates memory,
so much of this program is based on techniques others have discovered.
MCB format:
Byte Offset Description
-----------------------------------------------------------------------
0 contains byte 'M' if other MCBs higher
in memory, 'Z' if highest current MCB.
1 - 2 PID of block owner (identical to seg
addr of the program's program segment
prefix [PSP]).
3 - 4 length of block in 16-byte paragraphs.
Written for MASM 5.0 by Hardin Brothers for PCResource. The original
appeared in the July 1988 issue of their magazine. Entire contents of
the July 1988 issue (C) Copyright 1988 by
IDG Communications/Peterborough, Inc.
Hardin Brothers is a freelance programmer and technical writer. Write
to him at 280 N. Campus Ave., Upland, CA 91786. Enclose a self-
addressed, stamped envelope for a reply.
|
LF equ 0Ah ; linefeed char
CR equ 0Dh ; carriage return char
STDOUT equ 1 ; standard output device
PRINT macro text
mov AH, 40h ; INT 21h service 40h: write to device/file
mov BX, STDOUT ; send message to std output
lea DX, &text&msg ; DS:DX gets address of message string Xmsg
mov CX, &text&len ; get length of message
int 21h ; call DOS service
endm
EXIT macro val
mov AH, 4Ch ; INT 21h service 4Ch: exit from program
; also returns a value. replaces old INT 20h.
mov AL, val ; return value byte
int 21h
endm
.MODEL SMALL
.STACK
.DATA
ourpid dw ?
maxmem dw ?
basepsp dw ?
DosVer dw ?
DosV3 db 0
NoV1msg db 'This program cannot be used with DOS Ver. 1.x', CR, LF
NoV1len equ $-NoV1msg
VERmsg db 'Unknown DOS version -- cannot find head of memory'
db ' chain.', CR, LF
VERlen equ $-VERmsg
LERRmsg db "There apparently in an error with this program's"
db ' MCB.', CR, LF
LERRlen equ $-LERRmsg
ERRmsg db 'Memory allocation error -- Illegal MCB found.', CR, LF
ERRlen equ $-ERRmsg
HDRmsg db CR, LF, CR, LF
db ' MCB Block Block Block Block'
db ' Process', CR, LF
db ' Seg Seg Owner Size Type '
db ' Name ', CR, LF
db ' --------------------------------------------'
db '------------', CR, LF
HDRlen equ $-HDRmsg
mcbmsg db ' '
mcbadr db '0000 '
blkadr db '0000 '
blkown db '0000 '
blksiz db '0000 '
mcblen equ $-mcbmsg
CRLFmsg db CR, LF
CRLFlen equ $-CRLFmsg
PRGmsg db 'Prog '
PRGlen equ $-PRGmsg
ENVmsg db 'Envr '
ENVlen equ $-ENVmsg
OTHmsg db 'Data? '
OTHlen equ $-OTHmsg
hexlist db '0123456789ABCDEF'
memMSG db CR, LF, CR, LF
db 'Top of memory: '
memSIZ db '0000h.', CR, LF
db 'Next program will load at: '
memLOC db '0000h.', CR, LF, CR, LF
db 'Memory available: '
memAVL db '00000h bytes.', CR, LF, CR, LF
memlen equ $-memMSG
.CODE
start: cld
mov AX, @data
mov DS, AX ; set DS to "our" data seg
mov AH, 30h ; INT 21h service 30h: Get DOS Version
int 21h
mov DosVer, AX ; save entire version number
cmp al, 2 ; at least DOS version 2.x ?
jae verOK ; if so, continue...
PRINT NoV1 ; else complain about it
EXIT -1 ; and quit
verOK: sub AL, 2 ; test for version 3.x
mov DosV3, AL ; save result (0 or >0) as a flag
mov BX, 2 ; addr PSP:2 contains maxmem
mov AX, ES:[BX]
mov maxmem, AX
push ES
mov AX, ES ; get addr of "our" PSP
mov ourpid, AX ; save PSP
dec AX ; point to "our" MCB
mov ES, AX ; ES now points to our MCB
mov BX, 1 ; offset of PID in our MCB
inc AX ; AX contains our PID again
cmp AX, word ptr ES:[BX]
jz okay ; if not both the same, something's wrong!
PRINT LERR
EXIT -1
okay: pop ES ; ES points to our PSP
call findStart ; get head of memory chain
;----------------
; ES now points to the PSP in lowest memory, which ordinarily belongs
; to the first and only copy of COMMAND.COM. This PSP will be used as
; the place to begin the trek through memory.
;----------------
PRINT HDR ; output table column headers
rB1: push ES ; save ES
call printMCB ; print memory block info
mov AX, ES ; put MCB seg in AX
inc AX ; AX contains block seg
mov ES, AX ; ES points to memory block
call printTYPE ; print block type
jc rB2 ; skip to rB2 if type not ENVR
test DosV3, -1 ; using Version 3.x or later?
jz rB2 ; skip to rB2 if not
call printCMD ; print program name if found
rB2: PRINT CRLF ; terminate output line
pop ES ; ES points to current MCB
mov BX, 0 ; addr ES:BX points to block type (M or Z)
cmp byte ptr ES:[BX], 'Z' ; at tail of memory chain?
jz rB3 ; skip to rB3 if at tail
mov AX, ES ; get current MCB seg
inc AX ; AX contians block seg
mov BX, 3 ; addr ES:BX points to block size
add AX, ES:[BX] ; addr block size
mov ES, AX ; ES points to next MCB
jmp rB1 ; traverse chain. loop back.
rB3: call printMEM ; print memory info
EXIT 0 ; and terminate program
;----------------
; Find the head of the MCB list.
; This is the trickiest part of the program, as it is rarely
; documented. Method used was created by Ted Mirecki, and was published
; in the October 1987 issue of PC Tech Journal.
;----------------
findStart proc near
mov AX, 0 ; start with seg 0
mov ES, AX ; in ES
mov BX, 0C3h ; find seg of resident DOS
mov AX, ES:[BX] ; put the seg in AX
mov ES, AX ; ES points to resident DOS seg
mov AX, DosVer ; get DOS Version
xchg AH, AL ; Ver major in AH, minor in AL
mov BX, 10Ah ; offset for Ver 2.0
cmp AX, 0209h ; is this Ver 2.00 - 2.09 ?
jbe fS2 ; skip to fS2 if so
mov BX, 0F6h ; offset for Ver 2.1
cmp AX, 0213h ; is this Ver 2.10 - 2.19 ?
jbe fS2 ; skip to fS2 if so
cmp AL, 2 ; is this some unknown Ver 2.x ?
jne fS1 ; skip to fS1 if not
PRINT VER
EXIT -1
fS1: mov BX, 128h ; offset for Ver 3.0
cmp AX, 0309h ; is this Ver 3.00 - 3.09 ?
jbe fS2 ; skip to fS2 if so
mov BX, 22h ; offset for Ver 3.10 - 3.30
fS2: les BX, ES:[BX] ; get head of memory chain
ret
findStart endp
comment |
;----------------
; This is an alternate method of finding the head of the MCB list.
; This method will trace backwards to the last copy of COMMAND.COM
; installed in memory, and should work if the preceding method does
; not with your version of MSDOS.
;----------------
findStart proc near
mov BX, 16h ; addr PSP:16h points to parental PSP
fS1: mov AX, ES:[BX] ; get addr of parental PSP
cmp AX, 0 ; at tail of chain ? (1st test)
jz fS2 ; skip to fS2 if so
cmp AX, basepsp ; at tail of chain ? (2nd test)
jz fS2 ; skip to fS2 if so
mov basepsp, AX ; if not, save this value
mov ES, AX ; ES points to parental PSP
jmp fS1 ; loop back. traverse chain.
fS2: mov AX, ES ; get current PSP seg
dec AX ; AX is seg of MCB
mov ES, AX ; ES points to head of memory chain (1st MCB)
ret
findStart endp
|
;----------------
; ES points to a MCB. Print the information contained in the MCB.
;----------------
printMCB proc near
mov AX, ES ; AX is seg of MCB
lea DI, mcbadr ; DI points to message area
call hextoasc ; convert AX to ASCII
inc AX ; AX is block addr
lea DI, blkadr
call hextoasc
mov BX, 0 ; is this really a block ?
mov AL, ES:[BX] ; get first byte
cmp AL, 'M' ; part of chain ?
je pM1 ; skip to pM1 if so
cmp AL, 'Z' ; tail of chain ?
je pM1 ; skip to pM1 if so
PRINT ERR ; else notify error
EXIT -1 ; and terminate program
pM1: mov BX, 1 ; addr ES:BX points to block PID
mov AX, ES:[BX] ; get block PID
lea DI, blkown
call hextoasc
mov BX, 3 ; addr ES:BX points to block size
mov AX, ES:[BX] ; get block size
lea DI, blksiz
call hextoasc
PRINT mcb
ret
printMCB endp
;----------------
; ES points to a memory block. Determine if it is a PSP, ENVR, or
; unknown block. Note that the test for a PSP block checks if the
; first 4 bytes of the block are ASCII, so a data block could con-
; ceivably show up as a PSP.
;----------------
printTYPE proc near
mov BX, 0 ; addr ES:BX points to head of block
cmp byte ptr ES:[BX], 0CDh ; a PSP always starts at CDh
jnz typ1 ; no PSP here, skip to typ1
PRINT PRG
stc ; set carry flag
ret
typ1: mov CX, 4 ; test count of 4 chars
typ2: mov AL, ES:[BX] ; get a byte
cmp AL, ' ' ; check for CTRL code
jb typ3 ; skip to typ3 if so
cmp AL, 'a' ; check lowercase
jae typ3 ; skip to typ3 if so
inc BX ; otherwise point to next byte
loop typ2 ; and continue
PRINT ENV ; must be ENVR block type
clc ; clear carry flag
ret
typ3: PRINT OTH ; unknown block type
stc ; set carry flag
ret
printTYPE endp
;----------------
; ES points to an environment segment. Scan the segment and print the
; name of the program reported to own this environment. The procedure
; will only work with DOS Ver 3.0 and later.
;----------------
printCMD proc near
push DS ; save data seg
push ES ; copy ES
pop DS ; to DS
mov SI, 0 ; addr DS:SI points to environ text
mov CX, 8000h ; max. size of environ = 32768 bytes
mov AX, 0 ; init AX for search
pC1: lodsb ; get byte in AL, incr SI
or AX, AX ; set flags. test AX.
jz pC2 ; AX empty, found start of CMD
mov AH, AL ; AX not empty, save byte
loop pC1 ; and get next byte
pC1a: pop DS ; nothing found. restore DS
ret
pC2: lodsw ; get next word in AX, incr SI
cmp AX, 1 ; ought to be 0001h
jne pC1a ; skip to pC1a if not
mov DX, SI ; save start addr
mov CX, 0 ; ready byte count
pC3: lodsb ; get next byte, incr SI
inc CX ; incr byte count
cmp AL, ' ' ; end of string?
ja pC3 ; loop if not
mov BX, STDOUT ; get ready to print
mov AH, 40h ; INT 21h service 40h: write to file/device
int 21h
pop DS ; restore DS
ret
printCMD endp
;----------------
; Print memory block information.
;----------------
printMEM proc near
mov AX, maxmem
lea DI, memSIZ
call hextoasc
mov AX, ourpid
lea DI, memLOC
call hextoasc
mov AX, maxmem
sub AX, ourpid
lea DI, memAVL
call hextoasc
PRINT mem
ret
printMEM endp
;----------------
; Put value in AX as ASCII-Hex into location at addr DS:DI
; Note: procedure can only alter DI
;----------------
hextoasc proc near
push AX
push BX
push CX ; Save registers
push ES ; Save ES
push DS ; Copy DS
pop ES ; to ES
lea BX, hexlist ; addr DS:BX points to list of hex chars
xchg AH, AL ; swap bytes
call hexbyte ; process one byte
xchg AH, AL ; swap bytes back
call hexbyte ; process other byte
pop ES ; Restore ES
pop CX
pop BX
pop AX ; Restore registers
ret
hexbyte proc near
push AX ; save AL
and AL, 0F0h ; mask low nibble
mov CL, 4 ; bit shift count
shr AL, CL ; move high nibble down
xlat ; translate nibble to ASCII char
stosb ; store char at addr ES:DI, incr DI
pop AX ; restore AL
push AX ; save AX once more
and AL, 00Fh ; mask high nibble
xlat ; translate to ASCII
stosb ; store char in string
pop AX ; restore AX
ret
hexbyte endp
hextoasc endp
end start